package com.baitiaojun.common.utils.http;

import cn.hutool.core.util.ObjectUtil;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.concurrent.atomic.AtomicReference;

/**
 * 非阻塞响应式http请求工具类
 */
public class HttpUtils {

    /**
     * get请求, 无参数, 获取json对象
     * @param url
     * @param uri
     * @param clazz
     * @return
     * @param <T>
     */
    public static <T> Mono<ResponseEntity<T>> getEntity(String url, String uri, Class<T> clazz) {
       return buildGetReq(url, uri, MediaType.APPLICATION_JSON, null).toEntity(clazz);
    }

    /**
     * get请求, 有参数, 获取json对象
     * @param url
     * @param uri
     * @param params
     * @param clazz
     * @return
     * @param <T>
     */
    public static <T> Mono<ResponseEntity<T>> getEntity(String url, String uri, MultiValueMap<String, String> params, Class<T> clazz) {
        return buildGetReq(url, uri, MediaType.APPLICATION_JSON, params).toEntity(clazz); //获取全部响应对象
    }


    /**
     * get请求, 无参数, 获取对象流
     * @param url
     * @param uri
     * @param clazz
     * @return
     * @param <T>
     */
    public static <T> Flux<T> getFlux(String url, String uri, Class<T> clazz) {
        return buildGetReq(url, uri, MediaType.TEXT_EVENT_STREAM, null).bodyToFlux(clazz); //获取全部响应对象
    }

    /**
     * get请求, 有参数, 获取对象流
     * @param url
     * @param uri
     * @param clazz
     * @return
     * @param <T>
     */
    public static <T> Flux<T> getFlux(String url, String uri, MultiValueMap<String, String> params, Class<T> clazz) {
        return buildGetReq(url, uri, MediaType.TEXT_EVENT_STREAM, params).bodyToFlux(clazz); //获取全部响应对象
    }

    /**
     * 构建get请求
     * @param url
     * @param uri
     * @param params
     * @return
     */
    public static WebClient.ResponseSpec buildGetReq(String url, String uri, MediaType acceptType, MultiValueMap<String, String> params) {
        if (ObjectUtil.isNotNull(params)) {
            uri = UriComponentsBuilder.fromHttpUrl(uri).queryParams(params).build().toUriString();
        }
        return createWebClient(url).get()
                .uri(uri)
                .accept(acceptType) //响应类型
                .retrieve(); //声明如何提取响应
    }

    /**
     * post请求, 表单参数, 获取json
     * @param url
     * @param uri
     * @param formData
     * @param tClass
     * @return
     * @param <T>
     */
    public static <T> Mono<T> postForm(String url, String uri, MultiValueMap<String, String> formData, Class<T> tClass) {
        return buildPostReq(url, uri, MediaType.APPLICATION_FORM_URLENCODED, MediaType.APPLICATION_JSON, formData, null, tClass);
    }

    /**
     * post请求, json参数, 获取json
     * @param url
     * @param uri
     * @param body
     * @param tClass
     * @return
     * @param <T>
     */
    public static <T> Mono<T> postJson(String url, String uri, T body, Class<T> tClass) {
        return buildPostReq(url, uri, MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON, null, Mono.just(body), tClass);
    }

    /**
     * 构建post请求
     * @param url
     * @param uri
     * @param contentType
     * @param formData
     * @param bodyData
     * @param tClass
     * @return
     * @param <T>
     */
    public static <T> Mono<T> buildPostReq(String url, String uri, MediaType contentType, MediaType acceptType, MultiValueMap<String, String> formData, Mono<T> bodyData, Class<T> tClass) {
        WebClient.RequestBodySpec requestBodySpec = createWebClient(url).post().uri(uri).contentType(contentType).accept(acceptType);
        WebClient.RequestHeadersSpec<?> body = null;
        if (ObjectUtil.isNotNull(formData)) {
            body = requestBodySpec.bodyValue(formData);
        }
        if (ObjectUtil.isNotNull(bodyData)) {
            body = requestBodySpec.body(bodyData, tClass);
        }
        if (ObjectUtil.isNull(body)) {
            return null;
        }
        return body.retrieve().bodyToMono(tClass);
    }

    /**
     * 同步获取结果
     * @param mono
     * @return
     * @param <T>
     */
    public static <T> T sync(Mono<ResponseEntity<T>> mono) {
        AtomicReference<T> body = new AtomicReference<>();
        mono.subscribe(res -> body.set(res.getBody()));
        return body.get();
    }

    /**
     * 异步获取结果
     * @param mono
     * @return
     * @param <T>
     */
    public static <T> ResponseEntity<T> async(Mono<ResponseEntity<T>> mono) {
        return mono.block();
    }

    /**
     * 创建webclient实例
     * @param url
     * @return
     */
    public static WebClient createWebClient(String url) {
        return WebClient.create(url);
    }
}
